using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;

namespace ReportMaintenanceUtility
{
	public class ValidValues : GenericBase<ValidValues>
	{
		#region PRIVATE MEMBERS
		private object[]					_seed			= null;
		private DataSetReference			_dsRef			= null;
		private ParameterValueList			_parmValues		= null;
		private DefaultValueList			_defaultValues	= null;
		private ValueType					_vt				= ValueType.Undefined;
		private SourceType					_st				= SourceType.Undefined;
		private Dictionary<string, DataSet> _dsLibrary		= null;
		#endregion

		#region PUBLIC PROPERTIES

		public DataSetReference DataSetReference
		{
			get { return this._dsRef; }
			set
			{
				this.PrepStateChange("DataSet");
				this._dsRef = value;
				this._parmValues = null;
				this._defaultValues = null;
				this._st = SourceType.Queried;
			}
		}

		public ParameterValueList ValuesList
		{
			get { return this._parmValues; }
			set
			{
				this.PrepStateChange("ValuesList");
				this._parmValues = value;
				this._dsRef = null;
				this._st = SourceType.NonQueried;
			}
		}

		public DefaultValueList DefaultList
		{
			get { return this._defaultValues; }
			set
			{
				this.PrepStateChange("DefaultList");
				//this.NotifyPropertyChanged("DefaultList");
				this._defaultValues = value;
				this._dsRef = null;
				this._st = SourceType.NonQueried;
				//this._isDirty = true;
			}
		}

		public SourceType SourceType
		{
			get { return this._st; }
		}

		public ValueType ValueType
		{
			get { return this._vt; }
		}

		public bool HasData
		{
			get
			{
				switch (this._vt)
				{
					case ValueType.ValidValues:
						return (this._parmValues != null && this._parmValues.Count > 0);
					case ValueType.DefaultValues:
						return (this._defaultValues != null && this._defaultValues.Count > 0);
					default:
						return false;
				}
			}
		}
		#endregion

		#region CONSTRUCTOR(S)

		private ValidValues(ValidValues vv)
		{
			if (vv == null)
			{
				throw new ArgumentNullException();
			}
			else
			{
				this._vt = vv._vt;
				this._dsLibrary = vv._dsLibrary;
				switch (this._vt)
				{
					case ValueType.DefaultValues:
						DefaultValueType dvt = (DefaultValueType)vv.CreateSeedCopy();
						this._seed = dvt.Items;
						break;
					case ValueType.ValidValues:
						ValidValuesType vvt = (ValidValuesType)vv.CreateSeedCopy();
						this._seed = vvt.Items;
						break;
					default:
						throw new NullReferenceException("ValueType is null or undefined. Unable to create corresponding seed object.");
				}
			}
			base.CopyListeners(vv);
			this.Initialize();
		}

		public ValidValues(object[] items, Dictionary<string, DataSet> datasets, ValueType type)
		{
			if (items == null || items.Length == 0)
			{
				throw new ArgumentException();
			}
			else
			{
				this._seed = items;
				this._dsLibrary = datasets;
				this._vt = type;
				this.Initialize();
			}
		}

		protected override void Initialize()
		{
			for (int i = 0; i < this._seed.Length; i++)
			{
				//the type of objects in the items array is different depending on which parent object instantiated this object
				//both ValidValues and DefaultValues can contain a DataSetReferenceType in the array, but 
				//ValidValues uses ParameterValuesType objects for lists and DefaultValues uses ValuesType objects for its lists
				if (this._seed[i] is DataSetReferenceType)
				{
					DataSetReferenceType dsrt = (DataSetReferenceType)this._seed[i];
					if (dsrt != null && dsrt.ItemsElementName != null && dsrt.ItemsElementName.Length > 0)
					{
						this._dsRef = new DataSetReference(dsrt, this._dsLibrary);
						this._dsRef.PropertyChanged += this.PropertyChangedListener;
						this._st = SourceType.Queried;
					}
				}
				else if (this._seed[i] is ParameterValuesType)
				{
					ParameterValuesType pvts = (ParameterValuesType)this._seed[i];
					this._parmValues = new ParameterValueList(pvts);
					this._parmValues.ListChanged += this.ValuesListChangedListener;
					this._st = SourceType.NonQueried;
				}
				else if (this._seed[i] is ValuesType)
				{
					ValuesType vt = (ValuesType)this._seed[i];
					this._defaultValues = new DefaultValueList(vt);
					this._defaultValues.ListChanged += this.DefaultListChangedListener;
					this._st = SourceType.NonQueried;
				}
			}
		}
		#endregion

		#region PUBLIC METHODS
		public static bool AcceptsAnyValue(ValidValues vv)
		{
			foreach (ParameterValue pv in vv.ValuesList)
			{
				if (pv != null && (pv.Value == "*" || pv.Label == "*"))
				{
					return true;
				}
			}
			return false;
		}
		#endregion

		#region INTERFACE IMPLEMENTATION(S)

		public override List<ChangeLog> SaveState()
		{
			List<ChangeLog> log = new List<ChangeLog>();

			if (this._isDirty)
			{
				List<object> items = new List<object>();

				switch (this._st)
				{
					#region NON-QUERIED
					case SourceType.NonQueried:

						switch (this._vt)
						{
							case ValueType.ValidValues:

								if (this._parmValues != null && this._parmValues.Count > 0)
								{
									log.AddRange(this._parmValues.SaveState());
									items.Add(this._parmValues.GetSeedObject());
								}
								break;

							case ValueType.DefaultValues:

								if (this._defaultValues != null)
								{
									log.AddRange(this._defaultValues.SaveState());
									items.Add(this._defaultValues.GetSeedObject());
								}
								break;
						}

						break;
					#endregion

					#region QUERIED
					case SourceType.Queried:
						if (this._dsRef != null)
						{
							log.AddRange(this._dsRef.SaveState());
							items.Add(this._dsRef.GetSeedObject());
						}
						break;
					#endregion
				}

				this._seed = items.ToArray();
			}

			this.ClearState();
			return log;
		}
		
		public override bool EqualTo(ValidValues other)
		{
			if (other == null)
			{
				return false;
			}
			if (this.ValuesList == null && other.ValuesList == null)
			{
				return true;
			}
			if (this.ValuesList != null && other.ValuesList == null)
			{
				return false;
			}
			if (other.ValuesList != null)
			{
				if (ValidValues.AcceptsAnyValue(other))
				{
					return true;
				}
				else if (this.ValuesList == null)
				{
					return false;
				}
				else if (this.ValuesList.Count == 0 && other.ValuesList.Count == 0)
				{
					return true;
				}
				else
				{
					if (this.ValuesList.Count != other.ValuesList.Count)
					{
						return false;
					}
					else if (this.ValuesList.Count == 1 && this.ValuesList[0] != null)
					{
						return this.ValuesList[0].EqualTo(other.ValuesList[0]);
					}
					else
					{
						bool found = false;
						foreach (ParameterValue pv in this.ValuesList)
						{
							foreach (ParameterValue ov in other.ValuesList)
							{
								if (pv != null && pv.EqualTo(ov))
								{
									found = true;
									break;
								}
							}
							if (!found)
							{
								return false;
							}
							else
							{
								found = false;
							}
						}
						foreach (ParameterValue ov in other.ValuesList)
						{
							foreach (ParameterValue pv in other.ValuesList)
							{
								if (ov != null && ov.EqualTo(pv))
								{
									found = true;
									break;
								}
							}
							if (!found)
							{
								return false;
							}
							else
							{
								found = false;
							}
						}
					}
				}
			}
			if (this.DataSetReference == null && other.DataSetReference != null)
			{
				return false;
			}
			if (this.DataSetReference != null && !this.DataSetReference.EqualTo(other.DataSetReference))
			{
				return false;
			}
			return true;
		}

		public override void RestoreState()
		{
			if (this._copy != null)
			{
				this._dsRef = this._copy._dsRef;
				
				//TODO: need to determine how to re-wire all event handlers of child objects...
				this.CopyListeners(this._copy);

				this._parmValues = this._copy._parmValues;
				this._parmValues.ListChanged += this.ValuesListChangedListener;
				
				this._defaultValues = this._copy._defaultValues;
				this._defaultValues.ListChanged += this.DefaultListChangedListener;
				
				this._vt = this._copy._vt;
				this._st = this._copy._st;
			}

			this.ClearState();
		}

		public override ValidValues CreateDeepCopy()
		{
			if (this._copy != null)
			{
				return this._copy;
			}
			return new ValidValues(this);
		}
		
		public object[] GetSeedObject()
		{
			return this._seed;
		}

		#endregion

		protected void ValuesListChangedListener(object sender, ListChangedEventArgs e)
		{
			this.NotifyPropertyChanged(e.PropertyDescriptor.Name);
			this._isDirty = true;
		}

		protected void DefaultListChangedListener(object sender, ListChangedEventArgs e)
		{
			this.NotifyPropertyChanged(e.PropertyDescriptor.Name);
			this._isDirty = true;
		}

		public override object CreateSeedCopy()
		{
			List<object> items = new List<object>();

			for (int i = 0; i < this._seed.Length; i++)
			{
				if (this._seed[i] is DataSetReferenceType)
				{
					if (this._dsRef != null)
					{
						items.Add(this._dsRef.CreateSeedCopy());
					}
				}
				else if (this._seed[i] is ParameterValuesType)
				{
					ParameterValuesType pvts = new ParameterValuesType();
					List<ParameterValueType> pvtList = new List<ParameterValueType>();
					if (this._parmValues != null && this._parmValues.Count > 0)
					{
						foreach (ParameterValue pv in this._parmValues)
						{
							pvtList.Add((ParameterValueType)pv.CreateSeedCopy());
						}
					}
					pvts.ParameterValue = pvtList.ToArray();
					items.Add(pvts);
				}
				else if (this._seed[i] is ValuesType)
				{
					ValuesType vt = new ValuesType();
					List<string> values = new List<string>();
					if (this._defaultValues != null && this._defaultValues.Count > 0)
					{
						foreach(DefaultValue dv in this._defaultValues)
						{
							values.Add(dv.Value);
						}
					}
					vt.Value = values.ToArray();
					items.Add(vt);
				}
			}

			if (this._vt == ValueType.DefaultValues)
			{
				DefaultValueType dvt = new DefaultValueType();
				dvt.Items = items.ToArray();
				return dvt;
			}
			else if (this._vt == ValueType.ValidValues)
			{
				ValidValuesType vvt = new ValidValuesType();
				vvt.Items = items.ToArray();
				return vvt;
			}
			else
			{
				throw new NullReferenceException("ValueType is null or undefined. Unable to create corresponding seed object.");
			}
		}

		public void ResetDataSetLibrary(Dictionary<string, DataSet> list)
		{
			this._dsLibrary = list;
			if (this._dsRef != null)
			{
				if (this._dsLibrary.ContainsKey(this._dsRef.Name))
				{
					this._dsRef.Target = this._dsLibrary[this._dsRef.Name];
				}
				else
				{
					this._dsRef.Target = null;
				}
			}
		}
	}

	#region SUPPORTING ENUMERATIONS

	public enum ValueType
	{
		Undefined,
		DefaultValues,
		ValidValues
	}

	public enum SourceType
	{
		Undefined,
		Queried,
		NonQueried
	}

	#endregion
}
